﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using FarseerPhysics.Collision.Shapes;
using FarseerPhysics.Common;
using FarseerPhysics.Common.Decomposition;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using FarseerPhysics.Factories;
using FarseerPhysics.SamplesFramework;
using Flatland.AgentBehavior;
using Flatland.Elements;
using Flatland.GeneticProgramming.CoreTypes;
using Flatland.Geomerty;
using Flatland.Utilities;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

/**
* 
* Copyright (c) 2012 Anders Høst Kjærgaard | ahkj@itu.dk
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction, 
* including without limitation the rights to use, copy, modify, merge, publish, distribute, 
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial 
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

namespace Flatland {
    /// <summary>
    /// Agent class. Implements features of an agent, and behavioral commands e.g. for moving.
    /// </summary>
    public class Agent {

        #region fields

        //For debug
        public bool Control { get; set; }

        /// <summary>
        /// Point sensor A for women
        /// </summary>
        private Texture2D pointA;

        /// <summary>
        /// Point sensor B for women
        /// </summary>
        private Texture2D pointB;

        /// <summary>
        /// The sight range of an agent in pixels
        /// </summary>
        public const int AGENT_SIGHT_RANGE = 700;

        /// <summary>
        /// Default speed of agent.
        /// </summary>
        public const float AGENT_DEFAULT_SPEED = 5.5f;

        /// <summary>
        /// Max speed of agent.
        /// </summary>
        public const float AGENT_MAX_SPEED = 20.5f;

        /// <summary>
        /// The awareness range of the agent in pixels.
        /// </summary>
        public const int DEFAULT_AGENT_AWARENESS_RADIUS = 150;

        /// <summary>
        /// Default size of the Agent.
        /// </summary>
        public const int DEFAULT_SIZE = 50;

        /// <summary>
        /// Default angle of the agent
        /// </summary>
        public const int DEFAULT_ANGLE = 45;

        /// <summary>
        /// Max size of agent A Flatlander is at max 11 inches according to the novella. 
        /// This is around 825 pixels with 75 DPI so we to a tenth
        /// </summary>
        public const int MAX_SIZE = (825 / 10);

        /// <summary>
        /// Min size of agent
        /// </summary>
        public const int MIN_SIZE = 30;

        /// <summary>
        /// The maximum number of vertices. Approximates what is actually visible in the simulator. The max number would represent a perfect circle
        /// </summary>
        public const int MAX_VERTICES = 25;

        /// <summary>
        /// Point sensor offset on women.
        /// </summary>
        const float PointSensorOffset = 0.03f;

        /// <summary>
        /// Behavior of the agent. Is called on every update of the agent.
        /// </summary>
        public Behavior Behavior;

        /// <summary>
        /// Gets or sets the reports.
        /// </summary>
        /// <value>The genes.</value>
        public List<Report> Reports;

        /// <summary>
        /// Agent's awareness radius.
        /// </summary>
        public int AwarenessRadius = DEFAULT_AGENT_AWARENESS_RADIUS;

        /// <summary>
        /// Private. Agent speed.
        /// </summary>
        private float _speed = AGENT_DEFAULT_SPEED;

        /// <summary>
        /// Agent speed. Can be a value between 0 and the agent's max speed.
        /// </summary>
        public float Speed {
            get { return _speed; }
            set {
                if (0 <= value || value <= AGENT_MAX_SPEED)
                    _speed = value;
            }
        }

        /// <summary>
        /// Output from the Agent.
        /// </summary>
        public String Output;

        /// <summary>
        /// Size of the agent.
        /// </summary>
        private float _size = DEFAULT_SIZE;

        /// <summary>
        /// Size of the Agent. 
        /// </summary>
        public float Size {
            get { return _size; }
            set {
                if (MIN_SIZE <= value && value <= MAX_SIZE){
                    _size = value;
                }
                    
            }
        }

        /// <summary>
        /// Birthday of the Agent
        /// </summary>
        public DateTime BirthDay = DateTime.MinValue;

        /// <summary>
        /// Skin of the Agent.
        /// </summary>
        public Color Color = Color.Tomato;

        /// <summary>
        /// Texture material for the agent. This controls the look of its texture.
        /// </summary>
        public Texture2D TextureMaterial;

        /// <summary>
        /// Private field for direction
        /// </summary>
        private Vector2 _direction = new Vector2(0.0f, 0.01f);

        /// <summary>
        /// The agent's current moving direction.
        /// </summary>
        Vector2 Direction = new Vector2(0.0f, 0.01f);

        /// <summary>
        /// Private. Agent id.
        /// </summary>
        private string _id = "NO_ID";

        /// <summary>
        /// The agents unique id
        /// </summary>
        public string Id {
            get { return _id; }
            set { _id = value; }
        }

        /// <summary>
        /// Texture with graphical representation of the agent.
        /// </summary>
        public Texture2D Texture;

        /// <summary>
        /// Amount of food this agent has eaten.
        /// </summary>
        public int FoodEaten;

        /// <summary>
        /// True for every update where the agent can see food.
        /// </summary>
        public bool SeesFood;

        /// <summary>
        /// True for every update where the agent can sense food.
        /// </summary>
        public bool SensesFood;

        /// <summary>
        /// True for every update where the agent collides with a wall
        /// </summary>
        public bool FacesWall;

        /// <summary>
        /// True for every update where the agent can see a wall.
        /// </summary>
        public bool SeesWall;

        /// <summary>
        /// True for every update where the agent can sense a wall.
        /// </summary>
        public bool SensesWall;

        /// <summary>
        /// True for every update where the agent can see another Agent
        /// </summary>
        public bool SeesAgent;

        /// <summary>
        /// True for every update where the agent can sense another Agent
        /// </summary>
        public bool SensesAgent;

        /// <summary>
        /// The agents Body object used by the physics engine.
        /// </summary>
        public Body Body;

        /// <summary>
        /// Sight body relative to the agent.
        /// </summary>
        public Body Sight;

        /// <summary>
        /// Awareness body relative to the agent.
        /// </summary>
        public Body Awareness;

        /// <summary>
        /// Awareness texture. Can be used for debugging.
        /// </summary>
        Texture2D _awarenessTexture;

        /// <summary>
        /// The sight width of the agent.
        /// </summary>
        float _sightWidth;

        /// <summary>
        /// Sight texture. Can be used for debugging.
        /// </summary>
        Texture2D _sightTexture;

        /// <summary>
        /// Layer of the agent.
        /// </summary>
        public Layer Layer;

        /// <summary>
        /// Private. Vertices of the Agent.
        /// </summary>
        Vertices _vertices;

        /// <summary>
        /// Default 1 degree turn radian.
        /// </summary>
        const float _turnRadian = (float)(5 * Math.PI) / 180;

        /// <summary>
        /// Private. True if this Agent is a woman
        /// </summary>
        bool _isWoman;

        /// <summary>
        /// Get isWoman. True if this agent is a woman. 
        /// Women cannot be recognized by their number of vertices.
        /// </summary>
        public bool IsWoman { get { return _isWoman; } }

        /// <summary>
        /// Private. True if this Agent is a woman
        /// </summary>
        bool _isPerfectCircle;

        /// <summary>
        /// Get isIsPerfectCircle. True if this agent is a a perfectr circle. 
        /// Perfect circle cannot be recognized by their number of vertices.
        /// </summary>
        public bool IsPerfectCircle { get { return _isPerfectCircle; } }

        /// <summary>
        /// Women point sensors.
        /// </summary>
        FixedArray2<Body> _pointSensors;

        /// <summary>
        /// Agent rotation. 
        /// </summary>
        float _rotation;

        #endregion

        /// <summary>
        /// Initialize the Agent in Flatland, i.e. adding its bodies to the physics engine. 
        /// </summary>
        /// <param name="flatland">The flatland.</param>
        /// <param name="position">The position.</param>
        /// <param name="genome">The Behavior.</param>
        /// <param name="vertices">Number of vertices of the Agent has.</param>
        public void InitAgent(Vector2 position, int vertices = 3) {
            if (BirthDay == DateTime.MinValue)
                BirthDay = DateTime.Now;
            if (MAX_VERTICES < vertices)
                vertices = MAX_VERTICES;

            Body = BodyFactory.CreateBody(Program.FlatlandWorld.World, position);
            Body.Rotation = _rotation;
            ShapeAgent(vertices);
            Body.Friction = 1f;
            Body.CollisionCategories = Category.Agent;
            Body.CollidesWith = Category.All;// &~Category.Awareness;
            Body.BodyType = BodyType.Dynamic;
            Body.OnCollision += Collision;
            Body.UserData = this;

            //Continious collision detection, skal være slået til i FPE settings.cs (er den som standard) CCD
            //Body.IsBullet = true;

            //Tager kun en body.
            //JointFactory.Create

            InitSightSensor();
           // InitAwarenessSensor();
        }

        /// <summary>
        /// Sets the agent's geometrical shape. This includes creating a texture and attaching a fixture. 
        /// </summary>
        /// <param name="vertices">Vertices of shape</param>
        private void ShapeAgent(int vertices) {
            Debug.Assert(Body != null, "Body was null when creating the agent shape");
            Debug.Assert(1 < vertices, "Vertices for agent shape must be above 1");
            if (MAX_VERTICES < vertices)
                vertices = MAX_VERTICES;

            /*
             * Woman line shape. 
             * In reality a thin rectangle because this is much easier to handle
             * when dealing with interaction and possibly graphics.
             * Drawn as a vertical line so their direction will be with their plane side forwards, and not their pointy end.
             */
            if (vertices == 2) {
                float width = 0.01f;
                _vertices = WomanVerts(width);
                FixtureFactory.AttachRectangle(ConvertUnits.ToSimUnits(Size), width, 1f, new Vector2(ConvertUnits.ToSimUnits(Size / 2), width / 2), Body);
                for (int i = 0; i < _vertices.Count; i++) {
                    _vertices[i] = ConvertUnits.ToSimUnits(_vertices[i] * Size);
                }
                Texture = Program.FlatlandWorld.Assets.TextureFromVertices(_vertices, MaterialType.Blank, Color, 1f);
                InitPointSensors();
                _isWoman = true;
            }
            /*
             * Circle
             */
            else if (vertices == MAX_VERTICES) {
                Texture = Program.FlatlandWorld.Assets.CircleTexture(ConvertUnits.ToSimUnits(Size), MaterialType.Dots, Color, 1f, TextureMaterial);
                FixtureFactory.AttachCircle(ConvertUnits.ToSimUnits(Size), 1f, Body);
                _isPerfectCircle = true;
            }
            /*
             * Polygon shape
             */
            else {
                _vertices = Shaper.FlatlanderShape(vertices, Utils.DegreeToRadian(DEFAULT_ANGLE));
                //Apply size and to simulator conversion
                for (int i = 0; i < _vertices.Count; i++) {
                    _vertices[i] = ConvertUnits.ToSimUnits(_vertices[i] * Size);
                }
                Texture = Program.FlatlandWorld.Assets.TextureFromVertices(_vertices, MaterialType.Dots, Color, 1f, TextureMaterial);
                FixtureFactory.AttachCompoundPolygon(EarclipDecomposer.ConvexPartition(_vertices), 1f, Body);
            }
        }


        /// <summary>
        /// Get verts for a woman.
        /// </summary>
        public Vertices WomanVerts(float width){
            float halfWidth = width / 2;
            Vertices verts = new Vertices(4);
            verts.Add(new Vector2(-1, -halfWidth));
            verts.Add(new Vector2(-1, halfWidth));
            verts.Add(new Vector2(1, -halfWidth));
            verts.Add(new Vector2(1, halfWidth));
            return verts;
        }

        /// <summary>
        /// Init the agent's sight sensor
        /// </summary>
        private void InitSightSensor() {
            int centreDist = (AGENT_SIGHT_RANGE / 2) + Texture.Width / 2;
            Sight = BodyFactory.CreateBody(Program.FlatlandWorld.World, new Vector2(centreDist, centreDist));
            PolygonShape sightShape = new PolygonShape(0f);
            _sightWidth = Texture.Width;
            sightShape.SetAsBox(ConvertUnits.ToSimUnits(_sightWidth / 2), ConvertUnits.ToSimUnits((AGENT_SIGHT_RANGE / 2) + 5));
            Sight.CreateFixture(sightShape);
            Sight.BodyType = BodyType.Dynamic;
            Sight.CollisionCategories = Category.AgentSight;
            Sight.CollidesWith = Category.All;
            Sight.OnCollision += SightCollision;

            //Agent sight texture
            _sightTexture = new Texture2D(Program.FlatlandWorld.GraphicsDevice, (int)_sightWidth, AGENT_SIGHT_RANGE);
            Color[] data = new Color[(int)_sightWidth * AGENT_SIGHT_RANGE];
            for (int i = 0; i < data.Length; ++i)
                data[i] = Color.BlueViolet;
            _sightTexture.SetData(data);
        }

        /// <summary>
        /// Init point sensors on women. 
        /// Ie sensors on each end of the body that will kill other agents on collision
        /// </summary>
        private void InitPointSensors() {
            float pointRadius = 0.01f;
            Vector2 normToDirection = new Vector2((float)Math.Cos(_rotation), (float)Math.Sin(_rotation));
            Body a = BodyFactory.CreateCircle(Program.FlatlandWorld.World, pointRadius, 1f, Body.Position + (normToDirection * ((ConvertUnits.ToSimUnits(Size) + PointSensorOffset))));
            a.CollisionCategories = Category.WomanEnd;
            a.CollidesWith = Category.Agent;
            a.UserData = this;
            FixtureFactory.AttachCircle(pointRadius, 1f, a);
            a.IsSensor = true;
            a.OnCollision += OnPointSensorCollision;

            Body b = BodyFactory.CreateCircle(Program.FlatlandWorld.World, pointRadius, 1f, Body.Position - (normToDirection * ((ConvertUnits.ToSimUnits(Size) + PointSensorOffset))));
            b.CollisionCategories = Category.WomanEnd;
            b.CollidesWith = Category.Agent;
            b.UserData = this;
            FixtureFactory.AttachCircle(pointRadius, 1f, b);
            b.IsSensor = true;
            b.OnCollision += OnPointSensorCollision;

            pointA = Program.FlatlandWorld.Assets.CircleTexture(pointRadius, MaterialType.Dots, Color.Red, 1f);
            pointB = Program.FlatlandWorld.Assets.CircleTexture(pointRadius, MaterialType.Blank, Color.Blue, 1f);

            _pointSensors = new FixedArray2<Body>();
            _pointSensors[0] = a;
            _pointSensors[1] = b;
        }

        /// <summary>
        /// Collision event handler for Point sensors
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="contact"></param>
        /// <returns></returns>
        private bool OnPointSensorCollision(Fixture a, Fixture b, Contact contact) {
            if (a.CollisionCategories == Category.WomanEnd && b.CollisionCategories == Category.Agent && a.Body.UserData != b.Body.UserData) {
                Debug.Assert(((Agent)a.Body.UserData).IsWoman, "Agent making the kill is not a woman.");
                if (Utils.Random.Next(101) == 100) {
                    Console.WriteLine("Killed Flatlander: " + ((Agent)b.Body.UserData).Id);
                    Program.FlatlandWorld.ControlPanel.news_output = "Flatlander " + ((Agent)b.Body.UserData).Id + " was killed!";
                    Program.FlatlandWorld.KillAgent(((Agent)b.Body.UserData));
                }
            }
            return false;
        }

        /// <summary>
        /// Init the agent's awareness sensor
        /// </summary>
        private void InitAwarenessSensor() {
            //Awareness = BodyFactory.CreateBody(flatland.World, position);
            //CircleShape awarenessShape = new CircleShape(ConvertUnits.ToSimUnits(AwarenessRadius), 0f);
            //Awareness.BodyType = BodyType.Dynamic;
            //Awareness.CreateFixture(awarenessShape);
            //Awareness.CollisionCategories = Category.Awareness;
            //Awareness.CollidesWith = Category.All & ~Category.Sight;
            //Awareness.OnCollision += AgentAwarenessCollision;

            ////Awareness texture
            //_awarenessTexture = _flatland.Assets.CircleTexture(ConvertUnits.ToSimUnits(AwarenessRadius), MaterialType.Dots, Color.Yellow, 1f);
        }

        /// <summary>
        /// Respond the agent's shape, color and size.
        /// </summary>
        /// <param name="vertices"></param>
        /// <param name="color"></param>
        public void UpdateShape(int vertices, Color color, float size) {
            if (!IsWoman && vertices == 2) return; 
            Color = color;
            Size = size;
            RemoveAgent();
            InitAgent(Body.Position, IsWoman ? 2 : vertices);
        }

        private bool AgentAwarenessCollision(Fixture a, Fixture b, Contact contact) {
            switch (b.CollisionCategories) {
                case Category.Agent:
                    SensesAgent = true;
                    break;
                case Category.FOOD_CATEGORY:
                    SensesFood = true;
                    break;
                case Category.WALL_CATEGORY:
                    SensesWall = true;
                    break;
            }
            return false;
        }

        /// <summary>
        /// Agent's collision eventhandler
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="contact"></param>
        /// <returns></returns>
        private bool Collision(Fixture a, Fixture b, Contact contact) {
            switch (b.CollisionCategories) {
                case Category.FOOD_CATEGORY:
                    Eat(b.Body);
                    Output = "Ate food " + ((Food)b.Body.UserData).FoodName;
                    return false;
                case Category.WALL_CATEGORY:
                    FacesWall = true;
                    break;

                /*
                 * Colliding with other agents may casue death
                 */
                case Category.Agent:
                    if (b.Body.UserData == null) break;
                    Output = "Hit Flatlander " + ((Agent)b.Body.UserData).Id;
                    if (Utils.Random.Next(int.MaxValue) == 42) {
                        Console.WriteLine("Killed Flatlander: " + ((Agent)b.Body.UserData).Id);
                        Program.FlatlandWorld.ControlPanel.news_output = "Flatlander " + ((Agent)b.Body.UserData).Id + " was killed!";
                        Program.FlatlandWorld.KillAgent(((Agent)b.Body.UserData));
                    }
                    break;
            }
            return true;
        }

        /// <summary>
        /// Agents sight collision event handler.
        /// </summary>
        /// <param name="a">Fixture A.</param>
        /// <param name="b">Fixture B.</param>
        /// <param name="contact">The contact.</param>
        /// <returns></returns>
        private bool SightCollision(Fixture a, Fixture b, Contact contact) {
            switch (b.CollisionCategories) {
                case Category.FOOD_CATEGORY:
                    if (b.Body.UserData != null)
                        Output = "Saw " + ((Food)b.Body.UserData).FoodName;
                    SeesFood = true;
                    break;
                case Category.WALL_CATEGORY:
                    SeesWall = true;
                    break;
                case Category.Agent:
                    if (b.Body.UserData != null)
                        Output = "Saw Flatlander " + ((Agent)b.Body.UserData).Id;
                    SeesAgent = true;
                    break;
            }
            return false;
        }

        /// <summary>
        /// Make the agent eat
        /// </summary>
        /// <returns></returns>
        public void Eat(Body foodBody) {
            for (int i = 0; i < Program.FlatlandWorld.FoodReserve.Count; i++) {
                if (Program.FlatlandWorld.FoodReserve[i].FoodBody == foodBody) {
                    Output = "Ate " + Program.FlatlandWorld.FoodReserve[i].FoodName;
                    Program.FlatlandWorld.World.RemoveBody(Program.FlatlandWorld.FoodReserve[i].FoodBody);
                    Program.FlatlandWorld.FoodReserve.RemoveAt(i);
                    ++FoodEaten;
                    break;
                }
            }
        }

        private int i = 0;
        /// <summary>
        /// Update Agent in the game loop
        /// </summary>
        public void Update() {
            Behavior.Respond(ref Output);
            _rotation = (float)Math.Atan2(-Direction.X, Direction.Y);
            Body.Rotation = _rotation;
            Sight.Rotation = _rotation;

           // Body.Position += Direction * Speed;
            if (Body.Position.X < -FlatlandWorld.WorldSize) {
                Body.Position = new Vector2(FlatlandWorld.WorldSize, Body.Position.Y);
            }
            else if (FlatlandWorld.WorldSize < Body.Position.X) {
                Body.Position = new Vector2(-FlatlandWorld.WorldSize, Body.Position.Y);
            }

            if (Body.Position.Y < -FlatlandWorld.WorldSize) {
                Body.Position = new Vector2(Body.Position.X, FlatlandWorld.WorldSize);
            }
            else if (FlatlandWorld.WorldSize < Body.Position.Y) {
                Body.Position = new Vector2(Body.Position.X, -FlatlandWorld.WorldSize);
            }

            var forceVector = Vector2.Subtract(Body.Position, Math.Abs(Speed - 0f) < 0.000001f ? Body.Position : (Direction * Speed) + Body.Position);
            Body.ApplyForce(forceVector);
            // Awareness.Position = Body.Position;

            //Place the sight body just infront of the agent. A small gap of is added to constantly collide with itself
            int centreDist = (AGENT_SIGHT_RANGE / 2) + Texture.Width / 2;            
            Sight.Position = Body.Position + (1.1f * Direction * new Vector2(centreDist, centreDist));

            if (_pointSensors[0] != null && _pointSensors[1] != null) {
                Vector2 normToDirection = new Vector2((float)Math.Cos(_rotation), (float)Math.Sin(_rotation));
                _pointSensors[0].Position = Body.Position + (normToDirection * (ConvertUnits.ToSimUnits(Size) + PointSensorOffset));
                _pointSensors[1].Position = Body.Position - (normToDirection * (ConvertUnits.ToSimUnits(Size) + PointSensorOffset));
            }

            SeesFood = false;
            SeesAgent = false;
            SeesWall = false;
            SensesFood = false;
            SensesWall = false;
            SensesAgent = false;
        }

        public void Draw() {
            Program.FlatlandWorld.SpriteBatch.Draw(Texture, ConvertUnits.ToDisplayUnits(Body.Position),
                                           null,
                                           Color.White,
                                           Body.Rotation,
                                           new Vector2(Texture.Width / 2, Texture.Height / 2),
                                           1f,
                                           SpriteEffects.None,
                                           0f);
        }

        /// <summary>
        /// Make the agent move at its default speed.
        /// </summary>
        public void Move() {
            Speed = AGENT_DEFAULT_SPEED;
        }

        /// <summary>
        /// Make the Agent stop.
        /// </summary>
        public void Stop() {
            Speed = 0f;
        }

        /// <summary>
        /// Makes the agent turn 90 degress to its left
        /// </summary>
        public void TurnPerpendicularLeft()
        {
            float x = -Direction.Y;
            float y = Direction.X;
            Direction.X = x;
            Direction.Y = y;
        }

        /// <summary>
        /// Makes the agent turn 90 degress to its left
        /// </summary>
        public void TurnPerpendicularRight() {
            float x = Direction.Y;
            float y = -Direction.X;
            Direction.X = x;
            Direction.Y = y;
        }

        /// <summary>
        /// Make the agent turn.
        /// <param name="radians">Radians to turn by.</param>
        /// </summary>
        public void Turn(float radians) {
            var x = (float)(Math.Cos(radians) * Direction.X - (Math.Sin(radians) * Direction.Y));
            var y = (float)((Math.Sin(radians) * Direction.X) + Math.Cos(radians) * Direction.Y);
            Direction.X = x;
            Direction.Y = y;
        }

        /// <summary>
        /// Set the direction of the agent
        /// </summary>
        /// <param name="radians">Direction by radians</param>
        /// <returns></returns>
        public void SetDirection(float radians) {
            Direction.X = (float)Math.Cos(radians);
            Direction.Y = (float)Math.Sin(radians);
        }

        /// <summary>
        /// Make the agent turn right using its default turn degree
        /// </summary>
        public void TurnRight() {
            Turn(_turnRadian);
        }

        /// <summary>
        /// Make the agent turn left using its default turn degree
        /// </summary>
        public void TurnLeft() {
            Turn(-_turnRadian);
        }

        /// <summary>
        /// Representative number of vertices for the Agent.
        /// Women have two and perfect circles the max number of vertices for an Agent.
        /// </summary>
        /// <returns></returns>
        public int Vertices() {
            if (IsWoman)
                return 2;
            if (IsPerfectCircle)
                return MAX_VERTICES;
            return _vertices.Count;
        }

        /// <summary>
        /// Remove the agent from the environment.
        /// TODO remove from world population
        /// </summary>
        public void RemoveAgent() {
            Program.FlatlandWorld.World.RemoveBody(Body);
            Program.FlatlandWorld.World.RemoveBody(Sight);
            if (_pointSensors[0] != null || _pointSensors[1] != null) {
                Program.FlatlandWorld.World.RemoveBody(_pointSensors[0]);
                Program.FlatlandWorld.World.RemoveBody(_pointSensors[1]);
            }
            //    _flatland.World.RemoveBody(Awareness);
        }
    }
}
